from django.shortcuts import render
import pickle
import base64
# Create your views here.
from django_redis import get_redis_connection
from rest_framework import status
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response

from carts.serializers import CartSerializer, CartSKUSerializer, CartDeleteSerializer,CartSelectAllSerializer
from goods.models import SKU
from .constants import CART_COOKIE_EXPIRES


class CartView(GenericAPIView):
    """购物车"""
    serializer_class = CartSerializer
    # params:sku_id count selected
    # 因为要保存在cookie中,使用序列化器拿不到respons
    # 所以保存的代码要在视图中写

    # 不有view里面的方法进行身份认证,而是自己来认证
    def perform_authentication(self, request):
        # 执行具体请求方法前的身份认证关闭,由视图自己来执行身份认证
        pass

    # 增加购物车
    def post(self, request):
        # 保存购物车
        # 利用序列化器校验数据,request.data返回解析后的请求体数据
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        sku_id = serializer.validated_data['sku_id']
        count = serializer.validated_data['count']
        selected = serializer.validated_data['selected']

        # 自己来判断用户的身份
        try:
            user = request.user  # 没有认证会返回一个匿名用户
        except Exception:
            user = None

        # 保存
        # 如果用户存在,数据保存到redis, 排除前端返回匿名对象
        if user and user.is_authenticated:
            redis_conn = get_redis_connection('cart')
            # hincrby:reids,key不存在,创建一个值,会自动叠加不会覆盖
            # 购物车数据:redis hash
            pl = redis_conn.pipeline()
            pl.hincrby('cart_%s' % user.id, sku_id, count)
            if selected:
                # 购物车勾选:redis set
                pl.sadd('cart_selected_%s' % user.id, sku_id)
            pl.execute()
            return Response(serializer.data)
        else:
            # 如果用未登录,数据保存到cookie
            # 取出cookie中的数据
            cart_str = request.COOKIES.get('cart')
            # 如果cookie中有值,解析出来,为设置提供初始数据
            if cart_str:
                cart_str = cart_str.encode()
                cart_bytes = base64.b64decode(cart_str)
                # 解析成字典,提供给我们使用
                cart_dict = pickle.loads(cart_bytes)
            else:
                cart_dict = {}

            # 如果添加的商品存在,累加
            if sku_id in cart_dict:
                cart_dict[sku_id]['count'] += count
                cart_dict[sku_id]['selected'] = selected
            # 不存在,设置
            else:
                cart_dict[sku_id] = {
                    'count': count,
                    'selected': selected
                }
            cart_cookie = base64.b64encode(pickle.dumps(cart_dict)).decode()

            # 设置cookie
            response = Response(serializer.data)
            response.set_cookie('cart', cart_cookie, max_age=CART_COOKIE_EXPIRES)
            return response

    # 查询购物车
    def get(self, request):
        # 判断用户是否登陆
        try:
            user = request.user
        except Exception as e:
            user = None

        # 查询
        if user and user.is_authenticated:
            # 已经登陆,redis中查询,sku_id, count, selected
            redis_conn = get_redis_connection('cart')
            # 从数据库中查询出来数据,构建成统一的数据格式,方便统一处理
            redis_cart = redis_conn.hgetall('cart_%s'% user.id)
            redis_cart_selected = redis_conn.smembers('cart_selected_%s' % user.id)
            # 遍历redis_cart,形成统一的cart_dict
            cart_dict = {}
            for sku_id, count in redis_cart.items():
                cart_dict[int(sku_id)] = {
                    'count': int(count),
                    'selected': sku_id in redis_cart_selected
                }
        else:
            # 未登陆cookies中查询
            cookie_cart = request.COOKIES.get('cart')
            if cookie_cart:
                # 表示购物车中有数据
                # 解析数据
                cart_dict = pickle.loads(base64.b64decode(cookie_cart.encode()))
            else:
                # 没有数据,为空
                cart_dict ={}

        # 拿到sku_id, count, selected, 查询数据库,返回需要的数据
        # 拿到sku_id的集合
        # 查询出id 在sku_id中的查询集合
        sku_id_list = cart_dict.keys()
        sku_obj_list = SKU.objects.filter(id__in=sku_id_list)
        # 遍历sku_obj_list 向sku对象中添加count和selected属性

        for sku in sku_obj_list:
            sku.count = cart_dict[sku.id]['count']
            sku.selected = cart_dict[sku.id]['selected']

        # 序列化返回
        serializer = CartSKUSerializer(sku_obj_list, many=True)
        return Response(serializer.data)

     # 修改购物车

    #  修改购物车
    def put(self, request):
        # 使用序列化器校验数据,校验成功获取数据,request.data,post中的数据
        # 400请求错误,参数校验错误,序列化器错误
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        sku_id = serializer.validated_data['sku_id']
        count = serializer.validated_data['count']
        selected = serializer.validated_data['selected']
        # 判断用户的登陆状态
        try:
            user = request.user
        except Exception as e:
            user = None

        # 已经登陆, 保存
        if user and user.is_authenticated:
            redis_conn = get_redis_connection('cart')
            # 利用管道减少网络传输次数
            pl = redis_conn.pipeline()
            # 修改数量,那个用户的那个键,hash
            pl.hset('cart_%s' % user.id, sku_id, count)
            # 处理勾选状态,set
            if selected:
                #  需要勾选
                pl.sadd('cart_selected_%s' % user.id, sku_id)
            else:
                # 未勾选,删除
                pl.srem('cart_selected_%s' % user.id, sku_id)
            pl.execute()
            return Response(serializer.data)

        else:
            # 没有登陆修改cookies
            cookie_cart = request.COOKIES.get('cart')
            # 分为有数据和没有数据,分别处理
            if cookie_cart:
                # 表示购物车中有购物车数据
                cart_dict = pickle.loads(base64.b64decode(cookie_cart.encode()))
            else:
                cart_dict = {}
            response = Response(serializer.data)
            # 如果要修改的id在cookie里面就操作,不在里面是添加的
            if sku_id in cart_dict:
                cart_dict['sku_id'] = {
                    'count': count,
                    'selected': selected
                }
                # 反解析
                cart_cookie = base64.b64encode(pickle.dumps(cart_dict)).decode()
                # 设置cookie
                response.set_cookie('cart', cart_cookie, max_age=CART_COOKIE_EXPIRES)

            return response

    # 删除购物车
    def delete(self,request):
        # 传入的参数sku_id,返回204状态码
        # 校验参数,获取参数
        serializer = CartDeleteSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        sku_id = serializer.validated_data['sku_id']
        # 判断是否登陆
        try:
            user = request.user
        except Exception:
            user = None

        # 删除
        if user and user.is_authenticated:
            # 已经登陆的删除redis数据
            redis_conn = get_redis_connection('cart')
            pl = redis_conn.pipeline()
            # 删除hash,删除这个用户的这个键值对
            pl.hdel('cart_%s' % user.id, sku_id)
            # 删除set
            pl.srem('cart_selected_%s' % user.id, sku_id)
            pl.execute()
            return Response(status=status.HTTP_204_NO_CONTENT )
        # 没有登陆修改cookies中的数据
        else:
            # 没有登陆修改cookies
            cookie_cart = request.COOKIES.get('cart')
            # 分为有数据和没有数据,分别处理
            if cookie_cart:
                # 表示购物车中有购物车数据
                cart_dict = pickle.loads(base64.b64decode(cookie_cart.encode()))
            else:
                cart_dict = {}
            response = Response(serializer.data)
            # 如果要修改的id在cookie里面就操作,不在里面是添加的
            if sku_id in cart_dict:
                del cart_dict[sku_id]
                # 反解析
                cart_cookie = base64.b64encode(pickle.dumps(cart_dict)).decode()
                # 设置cookie
                response.set_cookie('cart', cart_cookie, max_age=CART_COOKIE_EXPIRES)

            return response


class CartSelectAllView(GenericAPIView):
    """购物车全选"""
    serializer_class = CartSelectAllSerializer

    def perform_authentication(self, request):
        # 执行具体请求方法前的身份认证关闭,由视图自己来执行身份认证
        pass

    # 修改所有购物车的select选项,所以是put方法
    def put(self, request):
        # 校验获取参数
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        selected = serializer.validated_data['selected']
        # 判断用户是否登陆
        try:
            user = request.user
        except Exception:
            user = None

        if user and user.is_authenticated:
            # 登陆,修改redis中的数据
            redis_conn = get_redis_connection('cart')
            redis_cart = redis_conn.hgetall('cart_%s' % user.id)
            # set里面添加所有hash的id就行了.
            # 拿到所有sku_id列表
            sku_id_list = redis_cart.keys()
            if selected:
                # 全选
                redis_conn.sadd('cart_selected_%s' % user.id, *sku_id_list)
            else:
                redis_conn.srem('cart_selected_%s' % user.id, *sku_id_list)
            return Response({'message': 'ok'})
        else:
            # 未登录修该cookie中的数据
            # cookies中找出数据,解析
            cookie_cart = request.COOKIES.get('cart')
            if cookie_cart:
                cart_dict = pickle.loads(base64.b64decode(cookie_cart.encode()))
            else:
                cart_dict = {}
            response = Response({'message': 'ok'})

            if cart_dict:
                for count_selected_dict in cart_dict.values():
                    count_selected_dict['selected'] = selected
                cart_cookie = base64.b64encode(pickle.dumps(cart_dict)).decode()
                response.set_cookie('cart', cart_cookie, max_age=CART_COOKIE_EXPIRES)

            return response



